import timber.util.Timer;

/**
	Listens for a list of events and broadcasts a message on activity events.
	The inactive event is fired when no mouse activity is detected for a specified
	interval of time. Activity is sent when the mouse becomes active again after an interval.
	
	If repeatsInactiveMessages is set to true, the inactive message is sent every inactiveInterval.
	If repeatsActiveMessages is true, the active message is sent on every activity event.

	@author Chandler McWilliams
	@version 2005-12-22
	
	@usage
	<code>
	var ao = new ActivityObserver(10000, myObject, 'onEvent');
	e = {};
	e.onActive = function() {
		trace('active');
	}
	e.onInactive = function() {
		trace('inactive');
	}
	ao.addListener(e);
	</code>
		
	this would trace "inactive" if myObject failed to broadcast an onEvent after 10 seconds.

*/
dynamic class timber.util.ActivityObserver
{
	private var inactiveInterval:Number;
	private var monitoredEvents:Array;
	private var monitoredObject;
	
	private var lastActivity:Number;
	private var lastEventDuration:Number;
	private var timer:Timer;
	private var objectIsActive:Boolean;
	private var doesRepeatInactiveMessages:Boolean;
	private var doesRepeatActiveMessages:Boolean;
	

	/**
		Constructor

		@param  newInactiveInterval	Length of time before inactive.
		@param  newObject					Object to monitor.
		@param  newMonitoredEvents		String or array of strings of events to listen for.
	**/
	function ActivityObserver(newInactiveInterval:Number, newObject:Object, newMonitoredEvents) {
		setInactiveInterval(newInactiveInterval);
		newObject.addListener(this);
		monitoredEvents = [];
		if (newMonitoredEvents instanceof Array) {
			for (var i=0; i<newMonitoredEvents.length; i++) {
				addMonitoredEvent(newMonitoredEvents[i]);
			}
		} else if (typeof newMonitoredEvents == 'string') {
			addMonitoredEvent(newMonitoredEvents);
		}
		
		AsBroadcaster.initialize(this);

		updateLastActivity();
		setLastEventDuration(0);
		setIsActive(true);
		timer = new Timer(mx.utils.Delegate.create(this, checkActivity),newInactiveInterval);
	}
	
	// ===========================================================
	// - ACTIVITY
	// ===========================================================
	/**
		Called when a monitored action occurs.
	**/	
	public function doActivity(Void):Void {
		updateLastActivity();
	}
	
	/**
		Checks for activity.
		Called by the timer every inactiveInterval
	*/
	private function checkActivity():Void {
		var currLast = lastActivity;		
		var now = new Date();
		lastActivity = now.getTime();
		setLastEventDuration(lastActivity-currLast);
		
		if (getLastEventDuration() >= inactiveInterval) {
			if (repeatsInactiveMessages() || isActive()) {
				setIsActive(false);
				broadcastInactive();
			}
		}
		
	}
	
	/**
		Called on activity.
		Sends active message if inactive or if repeatsActiveMessages is true.
	*/
	private function updateLastActivity():Void {
		// update the duration
		var currLast = lastActivity;		
		var now = new Date();
		lastActivity = now.getTime();
		setLastEventDuration(lastActivity-currLast);
		
		if (!isActive() || repeatsActiveMessages()) broadcastActive();
		setIsActive(true);
		resetTimer();
	}
	
	/**
		Resets inactivity timer.
	*/
	private function resetTimer():Void {
		timer.restart();
	}
	
	// ===========================================================
	// - FOR THE BROADCASTER
	// ===========================================================
	private function broadcastActive():Void {
		this.broadcastMessage("onActive", getLastEventDuration());
	}

	private function broadcastInactive():Void {
		this.broadcastMessage("onInactive", getLastEventDuration());
	}
		
	
	// ===========================================================
	// - ACCESSORS
	// ===========================================================
	private function setLastEventDuration(aValue:Number) { lastEventDuration = aValue;}
	public function getLastEventDuration():Number { return lastEventDuration;}
	
	public function setInactiveInterval(aValue:Number) { inactiveInterval = aValue; }
	public function getInactiveInterval():Number { return inactiveInterval;	}

	private function setIsActive(aValue:Boolean) { objectIsActive = aValue; }
	public function isActive():Boolean { return objectIsActive; }
	
	/**
		If true, an inactivity message is sent every inactiveInterval regardless or current state.
		
		@param Boolean
	*/
	public function setRepeatsInactiveMessages(aValue:Boolean) { doesRepeatInactiveMessages = aValue; }
	public function repeatsInactiveMessages():Boolean { return doesRepeatInactiveMessages; }

	/**
		If true, an activity message is sent every time activity occurs.
		
		@param Boolean
	*/
	public function setRepeatsActiveMessages(aValue:Boolean) { doesRepeatActiveMessages = aValue; }
	public function repeatsActiveMessages():Boolean { return doesRepeatActiveMessages; }

	public function getMonitoredObject():Object { return monitoredObject; }

	public function getMonitoredEvents():Array { return monitoredEvents; }

	/**
		Adds an event to the list of events being monitored.
		
		@param String name of the event to listen for.
	*/
	public function addMonitoredEvent(newEvent:String):Void {
		monitoredEvents.push(newEvent);
		var self = this;
		this[newEvent] = function() {
			self.doActivity();
		}
	}
}

